<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="李修倩在 Github 上的个人博客">
    <meta name="keyword" content="null">
    <meta name="theme-color" content="#600090">
    <meta name="msapplication-navbutton-color" content="#600090">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#600090">
    <link rel="shortcut icon" href="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-person-128.png">
    <link rel="alternate" type="application/atom+xml" title="Xiuqianli&#39;s blog" href="/atom.xml">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">
    <title>
        
        手写山寨spring基础功能 - MVC(2) - 异常处理和视图处理｜xiuqianli&#39;s blog
        
    </title>

    <link rel="canonical" href="https://981764793.gitee.io/2018/07/17/手写山寨spring基础功能-MVC-2-异常处理和视图处理/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/blog-style.css">

    <!-- Pygments Github CSS -->
    <link rel="stylesheet" href="/css/syntax.css">
</head>

<style>

    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }
</style>
<!-- hack iOS CSS :active style -->
<body ontouchstart="" class="animated fadeIn">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top " id="nav-top" data-ispost = "true" data-istags="false
" data-ishome = "false" >
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand animated pulse" href="/">
                <span class="brand-logo">
                    Xiuqianli&#39;s blog
                </span>
                's Blog
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <!-- /.navbar-collapse -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
					
                    
                        
							
                        <li>
                            <a href="/Tags/">Tags</a>
                        </li>
							
						
                    
					
					
                </ul>
            </div>
        </div>
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
//    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>

<!-- Main Content -->

<!--only post-->


<img class="wechat-title-img"
     src="http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80">


<style>
    
    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }

    
</style>

<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <div class="post-heading">
                    <h1>手写山寨spring基础功能 - MVC(2) - 异常处理和视图处理</h1>
                    
                    <span class="meta">
                         作者 Xiuqian Li
                        <span>
                          日期 2018-07-17
                         </span>
                    </span>
                    <div class="tags text-center">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="post-title-haojen">
        <span>
            手写山寨spring基础功能 - MVC(2) - 异常处理和视图处理
        </span>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- Post Container -->
            <div class="col-lg-8 col-lg-offset-1 col-sm-9 post-container">
                <h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>上一篇里记录了mvc的路由分发和参数绑定部分，mvc模块总共准备实现四个部分，这一篇准备记录一下视图处理和异常处理的实现过程。山寨spring部分的所有笔记都在我的专栏里。</p>
<h4 id="回顾WebLoader"><a href="#回顾WebLoader" class="headerlink" title="回顾WebLoader"></a>回顾WebLoader</h4><p>在上一篇里实现的WebLoader里完成了四个组件的加载，上一篇记录了其中两个，现在开始另外两个</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class WebLoader implements Loader &#123;</span><br><span class="line"></span><br><span class="line">    private static final Logger logger = Logger.getLogger(WebLoader.class);</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void doLoad(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line"></span><br><span class="line">        //装载视图处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ViewResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ViewResolver)o)</span><br><span class="line">                .forEach(ViewResolversContainer::add);</span><br><span class="line"></span><br><span class="line">        //装载参数处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ParameterResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ParameterResolver)o)</span><br><span class="line">                .forEach(ParameterResolverContainer::add);</span><br><span class="line"></span><br><span class="line">        //装载异常处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ExceptionResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ExceptionResolver)o)</span><br><span class="line">                .forEach(ExceptionResolverContainer::add);</span><br><span class="line"></span><br><span class="line">        //扫描被LSBean和LSRequestMapping修饰的类，装载路由</span><br><span class="line">        ClassUtil.getClassesByAnnotation(classSet, LSRequestMapping.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; clazz.isAnnotationPresent(LSBean.class))</span><br><span class="line">                .forEach(HandlerHelper::initRoute);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="视图处理"><a href="#视图处理" class="headerlink" title="视图处理"></a>视图处理</h4><p>视图处理装载过程是遍历扫描出来的beanSet，找出实现了ViewResolver接口的类，并且过滤掉抽象类。然后调用ObjectKit.getInstance尝试获取类实例，然后过滤掉获取实例失败的，最后添加到ViewResolversContainer里，ViewResolversContainer跟ParameterResolverContainer一样，都是内部一个LinkedList，实现一个遍历所有ViewResolver的handle方法</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class ViewResolversContainer &#123;</span><br><span class="line">    private static List&lt;ViewResolver&gt; viewResolverList = new LinkedList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    public static void add(ViewResolver viewResolver) &#123;</span><br><span class="line">        viewResolverList.add(viewResolver);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void handle(ActionHandler actionHandler, Object result,         HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line"></span><br><span class="line">        for (ViewResolver viewResolver : viewResolverList) &#123;</span><br><span class="line">            if (viewResolver.filter(actionHandler, result, request, response)) &#123;</span><br><span class="line">                viewResolver.handle(actionHandler, result, request, response);</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">public interface ViewResolver &#123;</span><br><span class="line"></span><br><span class="line">    boolean filter(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response);</span><br><span class="line">    void handle(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>ViewResolver与ParameterResolver一样也是需要两个方法，一个过滤（判断是否由这个Resolver处理），一个是真正的处理函数handle。直接看两个实现，分别处理模板视图（没有@LSResonseBody注解，并且返回值为字符串，默认jsp）和json视图（类或方法上有@LSResonseBody注解，默认使用Gson）</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class TemplateViewResolver implements ViewResolver &#123;</span><br><span class="line"></span><br><span class="line">    private TemplateEngine templateEngine = BeanContainer.getBean(&quot;DEFAULT_TEMPLATE_ENGINE&quot;);</span><br><span class="line"></span><br><span class="line">    public void setTemplateEngine(TemplateEngine templateEngine) &#123;</span><br><span class="line">        this.templateEngine = templateEngine;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public boolean filter(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line">        Method actionMethod = actionHandler.getActionMethod();</span><br><span class="line">        Class&lt;?&gt; controllerClass = actionHandler.getControllerClass();</span><br><span class="line">        //返回值为String并且类和方法上都没有LSResponseBody注解的才处理</span><br><span class="line">        return result instanceof String</span><br><span class="line">                &amp;&amp; !actionMethod.isAnnotationPresent(LSResponseBody.class)</span><br><span class="line">                &amp;&amp; !controllerClass.isAnnotationPresent(LSResponseBody.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void handle(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line">        templateEngine.render(request, response, result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class JsonViewResolver implements ViewResolver &#123;</span><br><span class="line"></span><br><span class="line">    private TemplateEngine jsonTemplateEngine = BeanContainer.getBean(&quot;DEFAULT_JSON_TEMPLATE_ENGINE&quot;);</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public boolean filter(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line">        Method actionMethod = actionHandler.getActionMethod();</span><br><span class="line">        Class&lt;?&gt; controllerClass = actionHandler.getControllerClass();</span><br><span class="line">        return actionMethod.isAnnotationPresent(LSResponseBody.class)</span><br><span class="line">                || controllerClass.isAnnotationPresent(LSResponseBody.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void handle(ActionHandler actionHandler, Object result, HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line">        jsonTemplateEngine.render(request, response, result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码都不难，语义理解。里面有用到TemplateEngine接口，这个接口是为了方便替换默认模板引擎而设计的，直接从BeanContainer里根据名字拿对应的实现对象，可以自己实现TemplateEngine接口，用json文件或者LSConfiguration注解类注入同名bean覆盖掉默认实现类。接下来看一下TemplateViewResolver里用到的JspTemplateEngine</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">@LSBean(&quot;DEFAULT_TEMPLATE_ENGINE&quot;)</span><br><span class="line">public class JspTemplateEngine implements TemplateEngine &#123;</span><br><span class="line"></span><br><span class="line">    private String viewPrefix = &quot;/WEB-INF&quot;;</span><br><span class="line">    private String viewSuffix = &quot;.jsp&quot;;</span><br><span class="line"></span><br><span class="line">    public void setViewPrefix(String viewPrefix) &#123;</span><br><span class="line">        this.viewPrefix = viewPrefix;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void setViewSuffix(String viewSuffix) &#123;</span><br><span class="line">        this.viewSuffix = viewSuffix;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void render(HttpServletRequest request, HttpServletResponse response, Object result) &#123;</span><br><span class="line">        String viewPath = getViewPath(String.valueOf(result));</span><br><span class="line">        try &#123;</span><br><span class="line">            request.getRequestDispatcher(viewPath).forward(request, response);</span><br><span class="line">        &#125; catch (ServletException | IOException e) &#123;</span><br><span class="line">            throw new LSMvcException(e.getCause());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private String getViewPath(String path) &#123;</span><br><span class="line">        String fullPath = &quot;/&quot; + viewPrefix + &quot;/&quot; + path + viewSuffix;</span><br><span class="line">        return fullPath.replaceAll(&quot;/+&quot;, &quot;/&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>因为在TemplateViewEngine里过滤了，所以result一定是String类型的，这里直接跟viewPrefix、viewSuffix拼接得到完整的视图路径后forward，这个异常不直接捕获，而是转成运行时异常往上抛，这也是一般web开发提倡的做法，检查型异常捕获后转成运行时异常往上抛。</p>
<p>再看JsonTemplateEngine的实现</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@LSBean(&quot;DEFAULT_JSON_TEMPLATE_ENGINE&quot;)</span><br><span class="line">public class JsonTemplateEngine implements TemplateEngine &#123;</span><br><span class="line">    private JsonEngine jsonEngine = new GsonJsonEngine();</span><br><span class="line"></span><br><span class="line">    public void setJsonEngine(JsonEngine jsonEngine) &#123;</span><br><span class="line">        this.jsonEngine = jsonEngine;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void render(HttpServletRequest request, HttpServletResponse response, Object result) &#123;</span><br><span class="line">        String jsonStr = null;</span><br><span class="line">        if (result instanceof String) &#123;</span><br><span class="line">            jsonStr = (String) result;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            jsonStr = jsonEngine.toJson(result);</span><br><span class="line">        &#125;</span><br><span class="line">        response.setContentType(&quot;application/json&quot;);</span><br><span class="line">        try &#123;</span><br><span class="line">            response.getWriter().write(jsonStr);</span><br><span class="line">        &#125; catch (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里用到了JsonEngine接口，只有一个toJson方法，是为了方便替换掉默认的json引擎，默认使用gson，处理过程就是设置application/json（这个好像对ie不太友好，其实之前也很迷，有时出现下载有时不出现的，设为text/html的话又直接出现文字，以前遇到这个问题自己都有点莫名奇妙的一会有问题一会没问题），然后把json字符串往response里写。</p>
<p>其实这两块自己也不知道算不算过度设计了，毕竟也没人会用这玩意，别说扩展了。。。</p>
<p>所以如果想实现一个自己的ViewResolver也很简单，实现完之后用LSConfiguration注解方法添加到ViewResolversContainer里就行。</p>
<h4 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h4><p>异常处理装载过程还有容器都与前面的类似，不多说这部分，</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public interface ExceptionResolver &#123;</span><br><span class="line"></span><br><span class="line">    Object handle(ActionHandler actionHandler, Exception exception, HttpServletRequest request</span><br><span class="line">            , HttpServletResponse response);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">public class ExceptionResolverContainer &#123;</span><br><span class="line">    private static List&lt;ExceptionResolver&gt; exceptionResolverList = new LinkedList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    public static boolean add(ExceptionResolver exceptionResolver) &#123;</span><br><span class="line">        return exceptionResolverList.add(exceptionResolver);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Object handle(ActionHandler actionHandler, Exception exception, HttpServletRequest request</span><br><span class="line">            , HttpServletResponse response) &#123;</span><br><span class="line">        for (ExceptionResolver exceptionResolver : exceptionResolverList) &#123;</span><br><span class="line">            Object result = exceptionResolver.handle(actionHandler, exception, request, response);</span><br><span class="line">            if (result != null) &#123;</span><br><span class="line">                return result;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>只要实现了ExceptionResolver接口的类都会被自动装载，处理时如果遇上了返回值不为null的处理器就直接返回处理结果，不再往下执行。接下来看一下TestExample里的实现类</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class GlobalException extends RuntimeException &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class GlobalExceptionResolver implements ExceptionResolver &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public Object handle(ActionHandler actionHandler, Exception exception, HttpServletRequest request, HttpServletResponse response) &#123;</span><br><span class="line">        if (exception instanceof GlobalException) &#123;</span><br><span class="line">            request.setAttribute(&quot;name&quot;, &quot;global exception:\n&quot; +exception.getMessage());</span><br><span class="line">            return &quot;test&quot;;</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>其实还可以在Exception里面判断一下返回值是json还是模板视图，返回不同的异常处理结果</p>
<h4 id="调用视图处理和异常处理"><a href="#调用视图处理和异常处理" class="headerlink" title="调用视图处理和异常处理"></a>调用视图处理和异常处理</h4><p>上面只是实现了一长串看上去没什么用的东西，实现完了当然得调用，现在就看看在哪用到这一长串东西。上一篇贴过LSDispatchServlet代码，现在再贴一次吧，写这篇笔记时想起一些东西又加了一些代码和注释。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class LSDispatchServlet extends HttpServlet &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException &#123;</span><br><span class="line">        req.setCharacterEncoding(&quot;UTF-8&quot;);</span><br><span class="line">        resp.setCharacterEncoding(&quot;UTF-8&quot;);</span><br><span class="line">        RequestContext.setRequest(req);</span><br><span class="line">        RequestContext.setResponse(resp);</span><br><span class="line">        String formatUrl = req.getRequestURI().replaceAll(&quot;/+&quot;, &quot;/&quot;);</span><br><span class="line">        ActionHandler actionHandler = HandlerContainer.get(formatUrl);</span><br><span class="line">//        System.out.println(req.getRequestURI());</span><br><span class="line">        if (actionHandler != null) &#123;</span><br><span class="line">            Object result = null;</span><br><span class="line">            Exception exception = null;</span><br><span class="line">            try &#123;</span><br><span class="line">                result = actionHandler.invoke(formatUrl, req, resp);//执行action方法</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">                exception = e;</span><br><span class="line">            &#125;</span><br><span class="line">            try &#123;</span><br><span class="line">                processDispatchResult(actionHandler, result, req, resp, exception);//处理结果,包括异常处理</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                throw new ServletException(e);//没有响应的异常处理器，接着抛个servlet容器</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            resp.sendError(404);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        resp.flushBuffer();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void processDispatchResult(ActionHandler actionHandler, Object result, HttpServletRequest request</span><br><span class="line">            , HttpServletResponse response, Exception exception) throws Exception &#123;</span><br><span class="line">        if (exception != null) &#123;</span><br><span class="line">            result = ExceptionResolverContainer.handle(actionHandler, exception, request, response);//遍历执行所有异常处理器，找到第一个有处理结果的</span><br><span class="line">            if (result == null) &#123;</span><br><span class="line">                throw exception;//遍历完之后异常没有被处理掉就直接往上抛</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        // 开始处理action执行结果</span><br><span class="line">        if (result != null) &#123;</span><br><span class="line">            ViewResolversContainer.handle(actionHandler, result, request, response);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            throw new LSMvcException(&quot;The action handle result is null: &quot; + ClassUtil.getFullMethodName(actionHandler.getActionMethod()));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>视图处理器和异常处理器都在actionHandle执行结束之后的processDispatchResult里调用，有异常就尝试处理异常，如果异常没有处理结果就抛给servlet容器，result不为null就调用视图处理器处理，没有就抛个没有执行结果的运行时异常。</p>
<h4 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h4><p>至此就实现了一个最基本的”spring-mvc”，当然功能、扩展性、健壮性没法比，但是基本的模样已经有了。mvc部分到此结束，下一篇开始jdbc部分（简单的orm，做出个mybatis的小小小雏形）。</p>

                <hr>
                

                <ul class="pager">
                    
                    <li class="previous">
                        <a href="/2018/07/18/ArrayList-HashMap源码泛读笔记/" data-toggle="tooltip" data-placement="top"
                           title="ArrayList HashMap源码泛读笔记">&larr; Previous Post</a>
                    </li>
                    
                    
                    <li class="next">
                        <a href="/2018/07/17/手写山寨spring基础功能-MVC-1-路由注册和参数绑定/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - MVC(1) - 路由注册和参数绑定">Next Post &rarr;</a>
                    </li>
                    
                </ul>

                

                


                <!--加入新的评论系统-->
                
            </div>

            <div class="hidden-xs col-sm-3 toc-col">
                <div class="toc-wrap">
                    <ol class="toc"><li class="toc-item toc-level-4"><a class="toc-link" href="#前言"><span class="toc-text">前言</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#回顾WebLoader"><span class="toc-text">回顾WebLoader</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#视图处理"><span class="toc-text">视图处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#异常处理"><span class="toc-text">异常处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#调用视图处理和异常处理"><span class="toc-text">调用视图处理和异常处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#结语"><span class="toc-text">结语</span></a></li></ol>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5 class="text-center">
                        <a href="/tags/">FEATURED TAGS</a>
                    </h5>
                    <div class="tags">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>
        </div>

    </div>
</article>







<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <br>
                <ul class="list-inline text-center">
                
                
                
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/asdf1234-97">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                

                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/xiuqianli1996">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Xiuqianli&#39;s blog 2018
                    <br>
                    <span id="busuanzi_container_site_pv" style="font-size: 12px;">PV: <span id="busuanzi_value_site_pv"></span> Times</span>
                    <br>
                    Theme by <a href="https://haojen.github.io/">Haojen Ma</a>
                </p>

            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/blog.js"></script>

<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("https://981764793.gitee.io/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>

<!-- Google Analytics -->



<!-- Baidu Tongji -->


<!-- swiftype -->
<script type="text/javascript">
  (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
  (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
  e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
  })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

  _st('install','null','2.0.0');
</script>

<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<!--wechat title img-->
<img class="wechat-title-img" src="null">
</body>

</html>
